package com.hjm.mall.config.handler;

import com.hjm.mall.common.Constants;
import com.hjm.mall.common.ServiceResultEnum;
import com.hjm.mall.config.exception.TokenVerifyException;
import com.hjm.mall.config.annotation.TokenToMallUser;
import com.hjm.mall.dao.MallUserMapper;
import com.hjm.mall.dao.MallUserTokenMapper;
import com.hjm.mall.entity.MallUser;
import com.hjm.mall.entity.MallUserToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * 这时自定义的参数解析器类，用于处理控制器方法中使用了@TokenToMallUser注解的参数
 * 参数解析器是Spring MVC框架中的一种机制，用于将请求中的数据绑定到控制器方法的参数上。自定义参数解析器可以让我们在处理请求时执行一些额外的逻辑或转换操作
 * 作用：TokenToMallUserMethodArgumentResolver会检查控制器方法参数是否被@TokenToMallUser注解标注。如果是，它将根据注解的逻辑，从请求中获取相关信息（比如token），进行处理并返回相应的对象，以便在控制器方法中使用。
 */
@Component
public class TokenToMallUserMethodArgumentResolver implements HandlerMethodArgumentResolver {

    @Autowired
    private MallUserMapper mallUserMapper;
    @Autowired
    private MallUserTokenMapper mallUserTokenMapper;

    public TokenToMallUserMethodArgumentResolver() {
    }

    /**
     * 实现了Spring MVC中的HandlerMethodArgumentResolver接口的类中的方法，用于判断是否支持对某个方法参数进行解析
     * supportsParameter方法接收一个MethodParameter对象作为参数，该对象表示控制器方法中的一个方法参数
     * 方法内部通过检查该方法参数是否带有TokenToMallUser注解来确定是否支持解析
     * 如果某个方法参数带有TokenToMallUser注解，那么supportsParameter方法会返回true，
     * 表示该参数可以被当前的解析器处理。否则，返回false，表示不支持对该参数进行解析。
     *
     * @param parameter
     * @return
     */
    public boolean supportsParameter(MethodParameter parameter) {
        if (parameter.hasParameterAnnotation(TokenToMallUser.class)) {
            return true;
        }
        return false;
    }

    /**
     * resolveArgument是HandlerMethodArgumentResolver接口中的方法之一，在Spring MVC中用于解析控制器方法的参数
     *
     * @param parameter：表示需要被解析的方法参数的元数据，包括参数类型、参数上的注解等信息。
     * @param mavContainer：用于处理ModelAndView的容器对象，可以通过该对象设置或获取ModelAndView相关的属性。
     * @param webRequest：表示当前的请求对象，提供了对请求相关的方法和属性的访问。
     * @param binderFactory：用于创建和管理WebDataBinder对象的工厂，可以用于绑定请求参数到目标对象。
     * @return
     */
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
        /**
         * 首先，parameter.getParameterAnnotation(TokenToMallUser.class)返回指定方法参数上的TokenToMallUser注解对象。
         * 如果该方法参数没有被标注TokenToMallUser注解，那么返回值将为null。
         * 接着，instanceof TokenToMallUser会判断返回的注解对象是否是TokenToMallUser注解的实例。
         * 如果是，则条件表达式的结果为true，表示该方法参数被标注了TokenToMallUser注解。
         */
        if (parameter.getParameterAnnotation(TokenToMallUser.class) instanceof TokenToMallUser) {
            MallUser mallUser = null;
            String token = webRequest.getHeader("token");   // 获取请求头的token
            // 检查token，是否为空，长度是不是设定的长度，如果是就检查token，通过token获取到回mallUser，返回mallUser；如果不是就返回 "未登录！" 的信息
            if (null != token && !"".equals(token) && token.length() == Constants.TOKEN_LENGTH) {
                // 有token就去获取到 mallUserToken 对象
                MallUserToken mallUserToken = mallUserTokenMapper.selectByToken(token);
                // 检查token是否过期，过期就返回 "无效认证！请重新登录！" 的信息
                if (mallUserToken == null || mallUserToken.getExpireTime().getTime() <= System.currentTimeMillis()) {
                    TokenVerifyException.fail(ServiceResultEnum.TOKEN_EXPIRE_ERROR.getResult());
                }
                // 通过 mallUserToken 对象的userId获取到 mallUser 对象
                mallUser = mallUserMapper.selectByPrimaryKey(mallUserToken.getUserId());
                // 如果获取不到 mallUser 对象说明用户被注销或者删除，返回 ”无效用户！请重新登录“ 的信息
                if (mallUser == null) {
                    TokenVerifyException.fail(ServiceResultEnum.USER_NULL_ERROR.getResult());
                }
                // 如果用户被锁定就返回 "用户已被禁止登录！" 的信息
                if (mallUser.getLockedFlag().intValue() == 1) {
                    TokenVerifyException.fail(ServiceResultEnum.LOGIN_USER_LOCKED_ERROR.getResult());
                }
                // 以上情况都没有出现就返回 mallUser 对象
                return mallUser;
            }
            TokenVerifyException.fail(ServiceResultEnum.NOT_LOGIN_ERROR.getResult());
        }
        return null;
    }

    public static byte[] getRequestPostBytes(HttpServletRequest request)
            throws IOException {
        int contentLength = request.getContentLength();
        if (contentLength < 0) {
            return null;
        }
        byte buffer[] = new byte[contentLength];
        for (int i = 0; i < contentLength; ) {
            int readlen = request.getInputStream().read(buffer, i,
                    contentLength - i);
            if (readlen == -1) {
                break;
            }
            i += readlen;
        }
        return buffer;
    }
}
